﻿@using System.Globalization
@using AntDesign.Core.JsInterop.Modules.Components
@inherits AntDesignTestBase

@code {
    [Fact]
    public void Renders_custom_placeholder()
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        string[] placeholders = new[] { "This is awesome", "Yeah, it is." };

        //Act
        var cut = Render<AntDesign.RangePicker<DateTime?[]>>
                    (@<RangePicker TValue="DateTime?[]" Placeholder="@(placeholders)" />);

        //Assert
        var inputs = cut.FindAll("input");

        for (var i = 0; i < inputs.Count; i++)
        {
            var input = inputs[i];
            input.GetAttribute("placeholder").Should().Be(placeholders[i]);
        }
    }

    [Fact]
    public void Preserves_placeholder_after_clear()
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        string[] placeholders = new[] { "This is awesome", "Yeah, it is." };
        var valueAfterClear = new DateTime?[] { null, null };
        var rangeValue = new DateTime?[] { new DateTime(2022, 6, 8), new DateTime(2022, 7, 8) };

        //Act
        var cut = Render<AntDesign.RangePicker<DateTime?[]>>
                  (@<RangePicker @bind-Value="rangeValue" Placeholder="@(placeholders)" />);

        cut.Instance.ClearValue();

        //Assert
        cut.Instance.Value.Should().BeEquivalentTo(valueAfterClear);
        var inputs = cut.FindAll("input");

        for (var i = 0; i < inputs.Count; i++)
        {
            var input = inputs[i];
            input.GetAttribute("placeholder").Should().Be(placeholders[i]);
        }
    }

    [Fact]
    public async Task Sets_disabled_time_when_range_dates_are_the_same()
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.Blur, _ => true);
        JSInterop.Setup<OverlayPosition>(JSInteropConstants.OverlayComponentHelper.AddOverlayToContainer, _ => true)
            .SetResult(new OverlayPosition());
        JSInterop.Setup<HtmlElement>(JSInteropConstants.DomInfoHelper.GetInfo, _ => true);

        var startValue = new DateTime(2022, 6, 8, 20, 20, 0);
        var endValue = new DateTime(2022, 6, 8, 20, 20, 30);
        var rangeValue = new DateTime?[] { startValue, endValue };

        var expectedDisabledStartHours = new int[] { 21, 22, 23 };
        var expectedDisabledStartMinutes = Enumerable.Range(21, 39).ToArray();
        var expectedDisabledStartSeconds = Enumerable.Range(31, 29).ToArray();

        var expectedDisabledEndHours = Enumerable.Range(0, 20);
        var expectedDisabledEndMinutes = Enumerable.Range(0, 20).ToArray();
        var expectedDisabledEndSeconds = Enumerable.Range(31, 29).ToArray();

        //Act
        var cut = Render<AntDesign.RangePicker<DateTime?[]>>
                  (@<RangePicker @bind-Value="rangeValue" ShowTime="true" />);

        var inputs = cut.FindAll("input").ToList();

        //Assert

        await inputs[0].ClickAsync(new MouseEventArgs());
        cut.Instance.DisabledHours.Invoke(startValue).Should().BeEquivalentTo(expectedDisabledStartHours);
        cut.Instance.DisabledMinutes.Invoke(startValue).Should().BeEquivalentTo(expectedDisabledStartMinutes);
        cut.Instance.DisabledSeconds.Invoke(startValue).Should().BeEquivalentTo(expectedDisabledStartSeconds);

        await inputs[1].ClickAsync(new MouseEventArgs());
        cut.Instance.DisabledHours.Invoke(endValue).Should().BeEquivalentTo(expectedDisabledEndHours);
        cut.Instance.DisabledMinutes.Invoke(endValue).Should().BeEquivalentTo(expectedDisabledEndMinutes);
        cut.Instance.DisabledSeconds.Invoke(endValue).Should().BeEmpty();
    }

    [Fact]
    public async Task Sets_empty_disabled_time_when_range_dates_are_not_the_same()
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.Blur, _ => true);
        JSInterop.Setup<OverlayPosition>(JSInteropConstants.OverlayComponentHelper.AddOverlayToContainer, _ => true)
            .SetResult(new OverlayPosition());
        JSInterop.Setup<HtmlElement>(JSInteropConstants.DomInfoHelper.GetInfo, _ => true);

        var rangeValue = new DateTime?[]
        {
            new DateTime(2022, 6, 8, 20, 20, 0),
            new DateTime(2022, 6, 9, 20, 20, 30)
        };

        //Act
        var cut = Render<AntDesign.RangePicker<DateTime?[]>>
                  (@<RangePicker @bind-Value="rangeValue" ShowTime="true" /> );

        var inputs = cut.FindAll("input").ToList();

        //Assert
        for (var i = 0; i < inputs.Count; i++)
        {
            await inputs[i].ClickAsync(new MouseEventArgs());
            cut.Instance.DisabledHours.Invoke(rangeValue[i]!.Value).Should().BeEmpty();
            cut.Instance.DisabledMinutes.Invoke(rangeValue[i]!.Value).Should().BeEmpty();
            cut.Instance.DisabledSeconds.Invoke(rangeValue[i]!.Value).Should().BeEmpty();
        }
    }

    [Fact]
    public void Ignores_keyboard_input_when_start_is_later_than_end()
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.InvokeTabKey, _ => true);

        var range = new DateTime[]
        {
            new DateTime(2022, 6, 8, 20, 20, 0),
            new DateTime(2022, 6, 8, 20, 20, 30)
        };
         
        var expectedStart = range[0];
        var expectedEnd = range[1];

        var format = "yyyy-MM-dd HH:mm:ss";
        string startValueAsString = expectedStart.ToString(format);
        string endValueAsString = expectedEnd.ToString(format);

        string keyboardInput = "2022-06-08 20:25:35";

        //Act
        var cut = Render<AntDesign.RangePicker<DateTime[]>>
                  (@<RangePicker @bind-Value="range" ShowTime="true" />);
 
        //Act
        var input = cut.FindAll("input");
        input[0].Input(keyboardInput);
        input[0].KeyDown("ENTER");

        //Assert
        cut.Instance.Value[0].Should().Be(expectedStart);
        input[0].GetAttribute("value").Should().Be(startValueAsString);

        cut.Instance.Value[1].Should().Be(expectedEnd);
        input[1].GetAttribute("value").Should().Be(endValueAsString);
    }

    [Fact]
    public void Ignores_keyboard_input_when_end_is_earlier_than_start()
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.InvokeTabKey, _ => true);

        var range = new DateTime[]
       {
            new DateTime(2022, 6, 8, 20, 20, 0),
            new DateTime(2022, 6, 8, 20, 20, 30)
       };

        var expectedStart = range[0];
        var expectedEnd = range[1];

        var format = "yyyy-MM-dd HH:mm:ss";
        string startValueAsString = expectedStart.ToString(format);
        string endValueAsString = expectedEnd.ToString(format);

        string keyboardInput = "2022-06-07 12:25:35";

        //Act
        var cut = Render<AntDesign.RangePicker<DateTime[]>>
                  (@<RangePicker @bind-Value="range" ShowTime="true" />);

        //Act
        var input = cut.FindAll("input");
        input[1].Input(keyboardInput);
        input[1].KeyDown("ENTER");

        //Assert
        cut.Instance.Value[0].Should().Be(expectedStart);
        input[0].GetAttribute("value").Should().Be(startValueAsString);

        cut.Instance.Value[1].Should().Be(expectedEnd);
        input[1].GetAttribute("value").Should().Be(endValueAsString);
    }

    [Theory]
    [InlineData("Enter", true)]
    [InlineData("Tab", true)]
    [InlineData("Enter", false)]
    [InlineData("Tab", false)]
    public async Task Key_applies_input_to_value(string key, bool showTime)
    {
        //Arrange
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);
        JSInterop.SetupVoid(JSInteropConstants.InvokeTabKey, _ => true);

        var range = new DateTime[]
        {
            new DateTime(2022, 1, 1, 0, 0, 0),
            new DateTime(2022, 1, 1, 1, 0, 0)
        };

        var expectedStart = new DateTime(2022, 6, 8, 0, 0, 0);
        var expectedEnd = new DateTime(2022, 6, 9, 0, 0, 0);

        if (showTime)
        {
            expectedStart = expectedStart.AddHours(20).AddMinutes(30).AddSeconds(5);
            expectedEnd = expectedEnd.AddHours(1).AddMinutes(50).AddSeconds(5);
        }

        var format = showTime ? "yyyy-MM-dd HH:mm:ss" : "yyyy-MM-dd";
        string startValueAsString = expectedStart.ToString(format);
        string endValueAsString = expectedEnd.ToString(format);

        //Act
        var cut = Render<AntDesign.RangePicker<DateTime[]>>
                  (@<RangePicker @bind-Value="range" ShowTime="showTime" />  );

        //Act
        var input = cut.FindAll("input");
        await input[0].InputAsync(new ChangeEventArgs() { Value = startValueAsString });
        await input[0].KeyDownAsync(new KeyboardEventArgs() { Key = key });
        await input[1].InputAsync(new ChangeEventArgs() { Value = endValueAsString });
        await input[1].KeyDownAsync(new KeyboardEventArgs() { Key = key });

        //Assert
        input = cut.FindAll("input");
        range[0].Should().Be(expectedStart);
        input[0].GetAttribute("value").Should().Be(startValueAsString);
        range[1].Should().Be(expectedEnd);
        input[1].GetAttribute("value").Should().Be(endValueAsString);
	}

    [Fact]
    public void ItShouldUseCustomSuffixIcon()
    {
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);

        var range = new DateTime[]
        {
            new DateTime(2022, 12, 5),
            new DateTime(2022, 12, 8)
        };

        var cut = Render<AntDesign.RangePicker<DateTime[]>>(
            @<RangePicker TValue="DateTime[]" @bind-Value="range">
                <SuffixIcon>
                    <Icon Type="ant-design" />
                </SuffixIcon>
            </RangePicker>
        );

        cut.Find(".ant-picker-suffix").MarkupMatches(
            @<span class="ant-picker-suffix">
                <span role="img" class=" anticon anticon-ant-design" id:ignore>
                    <svg focusable="false" width="1em" height="1em" fill="currentColor" style="pointer-events: none;" xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 1024 1024">
                        <path d="M716.3 313.8c19-18.9 19-49.7 0-68.6l-69.9-69.9.1.1c-18.5-18.5-50.3-50.3-95.3-95.2-21.2-20.7-55.5-20.5-76.5.5L80.9 474.2a53.84 53.84 0 0 0 0 76.4L474.6 944a54.14 54.14 0 0 0 76.5 0l165.1-165c19-18.9 19-49.7 0-68.6a48.7 48.7 0 0 0-68.7 0l-125 125.2c-5.2 5.2-13.3 5.2-18.5 0L189.5 521.4c-5.2-5.2-5.2-13.3 0-18.5l314.4-314.2c.4-.4.9-.7 1.3-1.1 5.2-4.1 12.4-3.7 17.2 1.1l125.2 125.1c19 19 49.8 19 68.7 0zM408.6 514.4a106.3 106.2 0 1 0 212.6 0 106.3 106.2 0 1 0-212.6 0zm536.2-38.6L821.9 353.5c-19-18.9-49.8-18.9-68.7.1a48.4 48.4 0 0 0 0 68.6l83 82.9c5.2 5.2 5.2 13.3 0 18.5l-81.8 81.7a48.4 48.4 0 0 0 0 68.6 48.7 48.7 0 0 0 68.7 0l121.8-121.7a53.93 53.93 0 0 0-.1-76.4z"></path>
                    </svg>
                </span>
            </span>
        );
    }

    [Fact]
    public void ItShouldUseDefaultSuffixIcon_WhenNoCustomSuffix()
    {
        JSInterop.SetupVoid(JSInteropConstants.AddPreventKeys, _ => true);

        var range = new DateTime[]
        {
            new DateTime(2022, 12, 5),
            new DateTime(2022, 12, 8)
        };

        var cut = Render<AntDesign.RangePicker<DateTime[]>>(@<RangePicker TValue="DateTime[]" @bind-Value="range" />);

        cut.Find(".ant-picker-suffix").MarkupMatches(
            @<span class="ant-picker-suffix">
                <span role="img" class=" anticon anticon-calendar" id:ignore>
                    <svg focusable="false" width="1em" height="1em" fill="currentColor" style="pointer-events: none;" xmlns="http://www.w3.org/2000/svg" class="icon" viewBox="0 0 1024 1024">
                        <path d="M880 184H712v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H384v-64c0-4.4-3.6-8-8-8h-56c-4.4 0-8 3.6-8 8v64H144c-17.7 0-32 14.3-32 32v664c0 17.7 14.3 32 32 32h736c17.7 0 32-14.3 32-32V216c0-17.7-14.3-32-32-32zm-40 656H184V460h656v380zM184 392V256h128v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h256v48c0 4.4 3.6 8 8 8h56c4.4 0 8-3.6 8-8v-48h128v136H184z"></path>
                    </svg>
                </span>
            </span>
        );
    }
}
